﻿using System;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MugenInjection.Attributes;
using MugenInjection.Infrastructure;
using Should;

namespace MugenInjectionTest.Infrastructure
{
    [TestClass]
    public class InjectorUtilsTest
    {
#if !NOEXPRESSION
        [TestMethod]
        public void TestParseExpression()
        {
#if NETFX_CORE
            var constructor = typeof(TestParseExpression).GetTypeInfo().DeclaredConstructors.First();
#else
            var constructor = typeof (TestParseExpression).GetConstructors()[0];
#endif

            ConstructorInfo constructorInfo;
            //Only constructor
            var injectionParameters = InjectorUtilsInternal.ParseNewExpression(context => new TestParseExpression("Test", context.Resolve<object>()), out constructorInfo);
            injectionParameters.Count.ShouldEqual(1);
            injectionParameters[0].MemberType.ShouldEqual(MemberTypes.Constructor);
            injectionParameters[0].GetValue(null).ShouldEqual("Test");
            injectionParameters[0].Name.ShouldEqual("param1");
            constructorInfo.ShouldEqual(constructor);
            
            //Constructor with method call
            injectionParameters = InjectorUtilsInternal.ParseNewExpression(context => new TestParseExpression(GetExampleString("Test"), context.Resolve<object>()), out constructorInfo);
            injectionParameters.Count.ShouldEqual(1);
            injectionParameters[0].MemberType.ShouldEqual(MemberTypes.Constructor);
            injectionParameters[0].GetValue(null).ShouldEqual("Test");
            injectionParameters[0].Name.ShouldEqual("param1");
            constructorInfo.ShouldEqual(constructor);

            //Constructor with property
            injectionParameters =
               InjectorUtilsInternal.ParseNewExpression(context => new TestParseExpression(GetExampleString("Test"), context.Resolve<object>())
               {
                   Property = "Property"
               }, out constructorInfo);
            injectionParameters.Count.ShouldEqual(2);
            injectionParameters[0].MemberType.ShouldEqual(MemberTypes.Property);
            injectionParameters[0].GetValue(null).ShouldEqual("Property");
            injectionParameters[0].Name.ShouldEqual("Property");

            injectionParameters[1].MemberType.ShouldEqual(MemberTypes.Constructor);
            injectionParameters[1].GetValue(null).ShouldEqual("Test");
            injectionParameters[1].Name.ShouldEqual("param1");
            constructorInfo.ShouldEqual(constructor);
            
            //Constructor with property and method call
            injectionParameters =
                InjectorUtilsInternal.ParseNewExpression(context => new TestParseExpression(GetExampleString("Test"), context.Resolve<object>())
                {
                    Property = GetExampleString("Property")
                }, out constructorInfo);
            injectionParameters.Count.ShouldEqual(2);
            injectionParameters[0].MemberType.ShouldEqual(MemberTypes.Property);
            injectionParameters[0].GetValue(null).ShouldEqual("Property");
            injectionParameters[0].Name.ShouldEqual("Property");

            injectionParameters[1].MemberType.ShouldEqual(MemberTypes.Constructor);
            injectionParameters[1].GetValue(null).ShouldEqual("Test");
            injectionParameters[1].Name.ShouldEqual("param1");
            constructorInfo.ShouldEqual(constructor);

            TestUtils.Catch<Exception>(() => InjectorUtilsInternal.ParseNewExpression(context => GetExampleString("etst"), out constructorInfo));
        }

        private string GetExampleString(string test)
        {
            return test;
        }
#endif

        [TestMethod]
        public void TestAttributeWork()
        {
#if NETFX_CORE
            var type = typeof(TestAttributeClass).GetTypeInfo();
            var fieldInfo = type.DeclaredFields.First();
#else
            Type type = typeof(TestAttributeClass);
            FieldInfo fieldInfo = type.GetFields()[0];
#endif
            //Class
            type.IsDefined(typeof(ObsoleteAttribute), true).ShouldBeTrue();
            type.IsDefined(typeof(CompilerGeneratedAttribute), true).ShouldBeTrue();
            type.IsDefined(typeof(TestMethodAttribute), true).ShouldBeFalse();
            type.IsDefined(typeof(InjectAttribute), true).ShouldBeFalse();

            //Field
            fieldInfo.IsDefined(typeof(ObsoleteAttribute), true).ShouldBeFalse();
            fieldInfo.IsDefined(typeof(CompilerGeneratedAttribute), true).ShouldBeFalse();
            fieldInfo.IsDefined(typeof(OptionalParameterAttribute), true).ShouldBeTrue();
            fieldInfo.IsDefined(typeof(InjectAttribute), true).ShouldBeTrue();

            //Class
            InjectorUtilsInternal.GetAttribute<ObsoleteAttribute>(type).ShouldNotBeNull();
            InjectorUtilsInternal.GetAttribute<CompilerGeneratedAttribute>(type).ShouldNotBeNull();
            InjectorUtilsInternal.GetAttribute<InjectAttribute>(type).ShouldBeNull();

            //Field
            InjectorUtilsInternal.GetAttribute<OptionalParameterAttribute>(fieldInfo).ShouldNotBeNull();
            InjectorUtilsInternal.GetAttribute<InjectAttribute>(fieldInfo).ShouldNotBeNull();
            InjectorUtilsInternal.GetAttribute<CompilerGeneratedAttribute>(fieldInfo).ShouldBeNull();
        }
    }

    [Obsolete]
    [CompilerGenerated]
    public class TestAttributeClass
    {
        [Inject]
        [OptionalParameter]
        public string Field;
    }

    public class TestParseExpression
    {
        public TestParseExpression(string param1, object param2)
        {
        }

        public object Property { get; set; }
    }
}